This invention relates to the creation of executable images in sophisticated computer languages such as LISP.
Executable images are preferred over suspended, nonexecutable images since properly created executable images are likely to be more robust in the face of operating system upgrades. Suspended image format is likely to break when created under one operating system version and used under another. In a multiple user environment, the read-only and system shareable portions of an executable image are mapped into memory once. Subsequent invocations of the executable image need only map in the static portions of the executable image. The executable image uses the operating system mapping mechanism to read in and make shareable the read-only and system shareable sections. Suspended images start with a very small "kernel" and require all of the data be mapped in by each user. None of the portions of the suspended image can be made shareable among multiple users. Executable images are faster to load and easier to distribute.
Traditional computer languages, such as FORTRAN, usually create single executable images for the reasons described above. The program is created by the user and compiled to create object code. The object code is then linked by a linker to create the final executable image. A suspended image, on the other hand, is essentially a snapshot (dump) of what memory looked like at the time the system was suspended. It comprises code and data in a format not suitable for use by a linker.
Since languages usually support operations, (e.g., trigonometric functions) that are not available on some processors, compiler developers implement "run-time support" functions as part of the compiler implementation project. These functions are normally contained in one or more run-time support libraries (RTL). Therefore, to generate an executable program, the linker must combine code which provides the selected functions from these libraries with the object code to form the executable image. As a result of this and other functions carried out by a linker, the object code will not be organized in memory exactly as it was in the original object file.
This alteration of the memory locations at which the object code is stored can cause problems since most programs contain interdependencies in the code. The unavoidable reorganization of memory that results when object code is linked will affect these interdependencies. For example, one instruction may require transfer of control to another point in the program (e.q., "go to"). FIG. 1A illustrates an object file containing N lines of object code. An interdependency can exist between point A on location 1 and point B on location 3. The exact displacement in memory between these points must be known for the code to execute properly. A special instruction section of the object file describes all of the interdependencies in the file which the linker uses in resolving the above mentioned dependencies.
FIG. 1B illustrates a traditional executable file. The lines of code are now separated due to the inclusion of functions such as the run time functions, shown at 10, and described above. Since points A and B are now a greater distance apart, the code must be revised to incorporate this change as well as other similar changes. These revisions are performed by the linker in accordance with the instructions contained in the instruction section of the object file.
In a language such as LISP, the compiled code is much more complex than object code created by more traditional languages. In a LISP system, compiled code can be freely mixed with interpreted code and data. Unlike a program developed in, e.q., FORTRAN, a LISP program contains such development tools as an interpreter, an editor, debugging tools, a windowing system, etc. As a result, a linker that could properly create an executable LISP image would have to be able to make extremely complex changes to the object code format and special instruction processing in order to ensure that the code would run in executable form. For example, in a program developed in LISP a source (interpreted) and/or a compiled file can redefine a form/object of an earlier loaded file. In other words when the system processes a file defining some function F and then processes a second file which redefines the function F, this serves to eliminate the old version of F. A linker would have to be able to recognize this fact at link time. Furthermore, in traditional languages, symbols merely represent locations in memory, while in LISP symbols include some functionality which would have to be evaluated by the linker. LISP objects are freely relocatable and the organization of LISP code may be varied when linked. It must all appear sequentially in the executable image just as it appeared in the object files. There are no linkers available that have the level of sophistication necessary to properly handle these and other complex LISP operations.
Therefore, a LISP programming environment is sold as a suspended image containing all of the LISP files (or modules), combined with a small executable "kernel" to provide access to the suspended image. The kernel is written in a low-level language such as Macro or BLISS. When a user wishes to use the LISP system, the small kernel is executed which serves to load all of the LISP environment into memory (i.e., the editor, interpreter etc.) The memory layout of a typical LISP system is shown in FIG. 2 and includes: a linked-transfer vector which is used to access portions of the low-level language environment from LISP and vice versa; the executable kernel described above; the run time library files; read only memory; static memory; two dynamic memory spaces; and a Bibop table. The Bibop table contains information about the type of data stored on each page of memory. Two dynamic memories are used to facilitate "garbage collections" which is the process of writing all valuable data from one memory space to another (e.q., from dynamic 1 to dynamic 0) to thereby eliminate unwanted data.